﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using MindTouch.Dream;

namespace WikiCopier
{
    class Program
    {
        static string sOldWebSiteName = "";
        static string sOldWebSiteLogin = "";
        static string sOldWebSitePassword = "";

        static string sNewWebSiteName = "";
        static string sNewWebSiteLogin = "";
        static string sNewWebSitePassword = "";

        static string sOldUrl = "";
        static string sNewUrl = "";

        static void Main(string[] args)
        {
            // get command line arguments or output help message
            if ((args.Length == 1 && ((args[0] == "/?") || (args[0] == "-?"))) || (args.Length != 8))
            {
                Console.Out.WriteLine("Please, use the following format of command line arguments:");
                Console.Out.WriteLine("(First wiki url address should be of the external website with the data which should be copied. Logins and passwords should be with admin rights. If wiki is public use \"no\" instead of the login and password. The last two arguments are used for links fixing.)");
                Console.Out.WriteLine("");
                Console.Out.WriteLine("WikiCopier.exe \"http://oldwikiwithdata.wik.is/@api/deki\" \"login\" \"password\" \"http://newwiki.somename.local/@api/deki\" \"login\" \"password\" \"oldwikiwithdata.wik.is\" \"newwiki.somename.local\" ");
                Console.Out.WriteLine("");
            }
            else
            {
                sOldWebSiteName = args[0];
                sOldWebSiteLogin = args[1];
                sOldWebSitePassword = args[2];

                sNewWebSiteName = args[3];
                sNewWebSiteLogin = args[4];
                sNewWebSitePassword = args[5];

                sOldUrl = args[6];
                sNewUrl = args[7];

                Console.Out.WriteLine("Old content deleting ...");
                DeleteOldPages();
                Console.Out.WriteLine("New content copying ...");
                Console.Write(String.Format("Percent complete {0}%\r", 0));
                CopyNewPages();
                Console.Out.WriteLine("");
                // Fix Embedded Images and Absolute links
                Console.Out.WriteLine("New content embedded images and absolute links fixing ...");
                Console.Write(String.Format("Percent complete {0}%\r", 0));
                FixNewPages();
                Console.Out.WriteLine("");
                // Copy File Descriptions
                Console.Out.WriteLine("File Descriptions coping ...");
                Console.Write(String.Format("Percent complete {0}%\r", 0));
                CopyFileDescriptions();
            }
        }

        private static void DeleteOldPages()
        {
            Plug p = Plug.New(sNewWebSiteName);
            p.At("users", "authenticate").WithCredentials(sNewWebSiteLogin, sNewWebSitePassword).Get();

            // skip root element
            DreamMessage document = null;
            document = p.At("pages").With("format", "xml").Get();
            XDoc currentDoc = document.AsDocument()["page"];
            currentDoc = currentDoc["subpages"];
            currentDoc = currentDoc["page"];
            string docID = "";

            do
            {
                if (currentDoc["@id"].IsEmpty)
                    break;
                docID = currentDoc["@id"].AsText;

                currentDoc = currentDoc.Next;
                p.At("pages", docID).With("recursive", "true").Delete();

            }
            while (!currentDoc.IsEmpty);
        }

        private static void CopyNewPages()
        {
            DreamMessage document = null;
            DreamMessage docContent = null;
            XDoc resp = null;
            double rez = 0;
            double cnt = 0;
            int i = 0;

            // get all pages from the WIKI.IS
            Plug p = Plug.New(sOldWebSiteName, new TimeSpan(0, 15, 0));
            if (sOldWebSiteLogin != "no")
            {
                p.At("users", "authenticate").WithCredentials(sOldWebSiteLogin, sOldWebSitePassword).Get();
            }
            document = p.At("pages").With("format", "xml").Get();

            // SCAN THESE PAGES
            var pages = ScanPages1(document.AsDocument());
            cnt = pages.Count();

            // GO THROUGH THESE PAGES AND COPY THEM
            Plug newp = Plug.New(sNewWebSiteName, new TimeSpan(0, 15, 0));
            newp.At("users", "authenticate").WithCredentials(sNewWebSiteLogin, sNewWebSitePassword).Get();
            foreach (Page oldPage in pages)
            {
                i++;
                //get old page content
                docContent = p.At("pages", oldPage.PageID.ToString(), "contents").With("format", "xhtml").Get();
                if (oldPage.PagePath != "=")
                {
                    try
                    {
                        // replace old absolute links
                        if (Regex.IsMatch(docContent.AsText(), sOldUrl))
                        {
                            DreamMessage dm1 = new DreamMessage(DreamStatus.Ok, null, MimeType.XML, docContent.AsText().Replace(sOldUrl, sNewUrl));
                            //string ss = String.Format("{0:s}", DateTime.Now);
                            resp = newp.At("pages", oldPage.PagePath, "contents").With("abort", "never").Post(dm1.AsDocument()).AsDocument();
                        }
                        else
                        {
                            resp = newp.At("pages", oldPage.PagePath, "contents").With("abort", "never").Post(docContent.AsDocument()).AsDocument();
                        }

                        //get list of attachments for this page from old wiki
                        document = p.At("pages", oldPage.PageID.ToString(), "files").Get();
                        XDoc currentFile = document.AsDocument()["file"];
                        do
                        {
                            if (currentFile["@id"].IsEmpty)
                                break;

                            // retrieve a file content
                            DreamMessage msg = p.At("files", currentFile["@id"].AsText).Get();
                            // upload file to the new wiki
                            // upload JPG images with correct mime type to fix IE8 error
                            if (currentFile["contents"]["@type"].AsText.ToLower() == "image/jpg")
                            {
                                DreamMessage newMsg = new DreamMessage(DreamStatus.Ok, null, MimeType.JPEG, msg.AsBytes());
                                if (msg.ContentDisposition != null)
                                {
                                    newp.At("pages", resp["page"]["@id"].AsText, "files", "=" + msg.ContentDisposition.FileName.Replace(' ', '_').Replace("^", "").Replace("/", "").Replace("..", ".")).Put(newMsg);
                                }
                                else
                                {
                                    newp.At("pages", resp["page"]["@id"].AsText, "files", "=" + currentFile["filename"].AsText.Replace(' ', '_').Replace("^", "").Replace("/", "").Replace("..", ".")).Put(newMsg);
                                }
                            }
                            else
                            {
                                if (msg.ContentDisposition != null)
                                {
                                    newp.At("pages", resp["page"]["@id"].AsText, "files", "=" + msg.ContentDisposition.FileName.Replace(' ', '_').Replace("^", "").Replace("/", "").Replace("..", ".")).Put(msg);
                                }
                                else
                                {
                                    newp.At("pages", resp["page"]["@id"].AsText, "files", "=" + currentFile["filename"].AsText.Replace(' ', '_').Replace("^", "").Replace("/", "").Replace("..", ".")).Put(msg);
                                }
                            }

                            currentFile = currentFile.Next;
                        }
                        while (!currentFile.IsEmpty);
                        document = null;
                        resp = null;
                    }
                    catch (DreamResponseException error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + oldPage.PagePath);
                    }
                    catch (Exception error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + oldPage.PagePath);
                    }
                }
                docContent = null;
                rez = (i / cnt) * 100;
                Console.Write(String.Format("Percent complete {0}%\r", Math.Floor(rez).ToString()));
            }
        }

        private static void FixNewPages()
        {
            DreamMessage document = null;
            DreamMessage docContent = null;
            XDoc resp = null;
            double rez = 0;
            double cnt = 0;
            int i = 0;

            // get all pages from the WIKI
            Plug p = Plug.New(sNewWebSiteName, new TimeSpan(0, 15, 0));
            if (sNewWebSiteLogin != "no")
            {
                p.At("users", "authenticate").WithCredentials(sNewWebSiteLogin, sNewWebSitePassword).Get();
            }
            document = p.At("pages").With("format", "xml").Get();

            // SCAN THESE PAGES
            var pages = ScanPages1(document.AsDocument());
            cnt = pages.Count();

            // GO THROUGH THESE PAGES AND VIEW CONTENT
            foreach (Page oldPage in pages)
            {
                i++;
                //get old page content
                docContent = p.At("pages", oldPage.PageID.ToString(), "contents").With("format", "xhtml").Get();
                if (oldPage.PagePath != "=")
                {
                    try
                    {
                        if (Regex.IsMatch(docContent.AsText(), "@api/deki/files"))
                        {
                            // replace old Image ID
                            string newDocText = docContent.AsText();
                            //string s = Regex.Replace("  abra  ", @"^\s*(.*?)\s*$", "$1");
                            MatchCollection matches = Regex.Matches(docContent.AsText(), @"\@api/deki/files/(\d+)/=([A-Za-z0-9_%20]+\.[A-Za-z]+)");
                            foreach (Match match in matches)
                            {
                                GroupCollection groups = match.Groups;
                                string oldString = groups[0].Value;
                                string oldId = groups[1].Value;
                                string oldFileName = groups[2].Value;
                                string newId = "";
                                string newString = "";

                                // get all attachments for this page and find new ID
                                document = p.At("pages", oldPage.PageID.ToString(), "files").Get();
                                XDoc currentFile = document.AsDocument()["file"];
                                do
                                {
                                    if (currentFile["@id"].IsEmpty)
                                        break;

                                    if ((currentFile["filename"].AsText.ToLower() == oldFileName.ToLower()) || (currentFile["filename"].AsText.ToLower() == oldFileName.ToLower().Replace("%20", "_")))
                                    {
                                        newId = currentFile["@id"].AsInt.ToString();
                                        break;
                                    }

                                    currentFile = currentFile.Next;
                                }
                                while (!currentFile.IsEmpty);

                                if (newId != "")
                                {
                                    newString = oldString.Replace("/" + oldId + "/", "/" + newId + "/");
                                    newDocText = newDocText.Replace(oldString, newString);
                                }
                            }

                            // Update page content
                            DreamMessage dm1 = new DreamMessage(DreamStatus.Ok, null, MimeType.XML, newDocText);
                            string ss = String.Format("{0:s}", DateTime.Now);
                            resp = p.At("pages", oldPage.PagePath, "contents").With("abort", "never").With("edittime", ss).Post(dm1.AsDocument()).AsDocument();
                        }
                        document = null;
                        resp = null;
                    }
                    catch (DreamResponseException error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + oldPage.PagePath);
                    }
                    catch (Exception error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + oldPage.PagePath);
                    }
                }
                docContent = null;
                rez = (i / cnt) * 100;
                Console.Write(String.Format("Percent complete {0}%\r", Math.Floor(rez).ToString()));
            }
        }

        private static void CopyFileDescriptions()
        {
            DreamMessage document = null;
            DreamMessage oldFiles = null;
            DreamMessage newFiles = null;
            double rez = 0;
            double cnt = 0;
            int i = 0;

            // get all pages from the WIKI.IS
            Plug p = Plug.New(sOldWebSiteName, new TimeSpan(0, 15, 0));
            if (sOldWebSiteLogin != "no")
            {
                p.At("users", "authenticate").WithCredentials(sOldWebSiteLogin, sOldWebSitePassword).Get();
            }
            document = p.At("pages").With("format", "xml").Get();

            // SCAN OLD PAGES
            var oldPages = ScanPages1(document.AsDocument());

            // GO THROUGH THESE PAGES AND COPY THEM
            Plug newp = Plug.New(sNewWebSiteName, new TimeSpan(0, 15, 0));
            newp.At("users", "authenticate").WithCredentials(sNewWebSiteLogin, sNewWebSitePassword).Get();
            document = newp.At("pages").With("format", "xml").Get();

            // SCAN NEW PAGES
            var pages = ScanPages1(document.AsDocument());
            cnt = pages.Count();

            //System.Collections.Generic.Dictionary<string, string>
            foreach (Page newPage in pages)
            {
                i++;
                if (newPage.PagePath != "=")
                {
                    try
                    {
                        foreach (Page oldPage in oldPages)
                        {
                            // get original page from old wiki
                            if (oldPage.PagePath == newPage.PagePath)
                            {
                                //get list of attachments for the NEW wiki
                                newFiles = newp.At("pages", newPage.PageID.ToString(), "files").Get();

                                //get list of attachments for the OLD wiki
                                oldFiles = p.At("pages", oldPage.PageID.ToString(), "files").Get();

                                XDoc currentFile = newFiles.AsDocument()["file"];
                                do
                                {
                                    int isBR = 0;

                                    if (currentFile["@id"].IsEmpty)
                                        break;

                                    // get list of old files
                                    XDoc tmpFile = oldFiles.AsDocument()["file"];
                                    do
                                    {
                                        if (tmpFile["@id"].IsEmpty)
                                            break;

                                        if (currentFile["filename"].AsText == tmpFile["filename"].AsText.Replace(' ', '_').Replace("^", "").Replace("/", "").Replace("..", "."))
                                        {
                                            isBR = 1;
                                            // retrieve a old file description
                                            DreamMessage msg = p.At("files", tmpFile["@id"].AsText, "description").Get();

                                            if (msg.AsText() != "")
                                            {
                                                // upload file description to the new wiki
                                                DreamMessage nMsg = DreamMessage.Ok(MimeType.TEXT, msg.AsText());
                                                newp.At("files", currentFile["@id"].AsText, "description").Put(nMsg);
                                            }

                                            break;
                                        }
                                        if (isBR == 1)
                                            break;

                                        tmpFile = tmpFile.Next;
                                    }
                                    while (!tmpFile.IsEmpty);

                                    currentFile = currentFile.Next;
                                }
                                while (!currentFile.IsEmpty);

                                break;
                            }
                        }

                        document = null;
                        newFiles = null;
                        oldFiles = null;
                    }
                    catch (DreamResponseException error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + newPage.PagePath);
                    }
                    catch (Exception error)
                    {
                        Console.Out.WriteLine(error.ToString());
                        Console.Out.WriteLine("The error was occured on the following page: " + newPage.PagePath);
                    }
                }
                rez = (i / cnt) * 100;
                Console.Write(String.Format("Percent complete {0}%\r", Math.Floor(rez).ToString()));
            }
        }

        public static List<Page> ScanPages1(XDoc document)
        {
            var pagesList = new List<Page>();
            return ScanPagesRecursive1(document, pagesList, 0);
        }

        public static List<Page> ScanPagesRecursive1(XDoc document, List<Page> pages, int parentPageID)
        {
            XDoc currentDoc = document["page"];
            do
            {
                if (currentDoc["@id"].IsEmpty)
                    break;
                pages.Add(new Page()
                {
                    PageID = currentDoc["@id"].AsInt.Value,
                    PageURL = currentDoc["@href"].AsText,
                    ParentPageID = parentPageID,
                    PageTitle = currentDoc["title"].AsText.Replace(' ', '_'),
                    PagePath = "=" + currentDoc["path"].AsText.Replace("/", "%252f")
                });
                if (!currentDoc["subpages"].IsEmpty)
                    ScanPagesRecursive1(currentDoc["subpages"], pages, currentDoc["@id"].AsInt.Value);

                currentDoc = currentDoc.Next;
            }
            while (!currentDoc.IsEmpty);
            return pages;
        }

    }
}
